package org.thingsboard.server.dao.timeseries;

import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.*;
import org.thingsboard.server.dao.model.sqlts.influx.ts.TsKv;
import org.thingsboard.server.dao.sqlts.AggregationTimeseriesDao;
import org.thingsboard.server.dao.util.DateUtils;
import org.thingsboard.server.dao.util.InfluxTsDao;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author: sanhoitou[sanhoitou@hotmail.com]
 * @Date: 2021/5/25
 */
@Component
@Slf4j
@InfluxTsDao
public class InfluxBaseTimeseriesDao extends AbstractInfluxBaseTimeseriesDao implements TimeseriesDao, AggregationTimeseriesDao {
    @Override
    public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, ReadTsKvQuery query) {
        String order = query.getOrder();
        int limit = query.getLimit();
        String startTs = DateUtils.timstamp2DateTimeStr(query.getStartTs());
        String endTs = DateUtils.timstamp2DateTimeStr(query.getEndTs());
        String wheres = String.format("\"key\"=\'%s\',\"entity_id\"=\'%s\'" , query.getKey() , entityId.getId().toString());
        List<TsKv> tsKv = super.fetchResults("*", "ts_kv" ,wheres,startTs ,endTs , order , String.valueOf(limit) , TsKv.class);
        List<TsKvEntry> entries = new ArrayList<>();
        for (TsKv kv : tsKv) {
            TsKvEntry tsKvEntry = conversionTsKv(kv);
            entries.add(tsKvEntry);
        }
        return Futures.immediateFuture(entries);
    }

    @Override
    public ListenableFuture<List<TsKvEntry>> findAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {
        List<TsKv> tsKvs = new ArrayList<>();
        for (ReadTsKvQuery query : queries) {
            String wheres = String.format("\"key\"=\'%s\',\"entity_id\"=\'%s\'" , query.getKey() , entityId.getId().toString());
            String order = query.getOrder();
            int limit = query.getLimit();
            String startTs = DateUtils.timstamp2DateTimeStr(query.getStartTs());
            String endTs = DateUtils.timstamp2DateTimeStr(query.getEndTs());
            tsKvs.addAll(super.fetchResults("*", "ts_kv", wheres, startTs, endTs, order, String.valueOf(limit), TsKv.class));
        }
        List<TsKvEntry> entries = new ArrayList<>();
        for (TsKv kv : tsKvs) {
            TsKvEntry tsKvEntry = conversionTsKv(kv);
            entries.add(tsKvEntry);
        }
        return Futures.immediateFuture(entries);
    }

    @Override
    public ListenableFuture<Void> save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
        TsKv tsKv = new TsKv();
        tsKv.setEntityId(entityId.getId().toString());
        tsKv.setKey(tsKvEntry.getKey());
        switch (tsKvEntry.getDataType()) {
            case BOOLEAN:
                tsKv.setBoolV(tsKvEntry.getBooleanValue().get());
                break;
            case LONG:
                tsKv.setLongV(tsKvEntry.getLongValue().get());
                break;
            case DOUBLE:
                tsKv.setDblV(tsKvEntry.getDoubleValue().get());
                break;
            case JSON:
                tsKv.setJsonV(tsKvEntry.getJsonValue().get());
                break;
            case STRING:
                tsKv.setStrV(tsKvEntry.getStrValue().get());
                break;
        }
        if (!tsKvEntry.getKey().equals("totalMsgs")
                && !tsKvEntry.getKey().equals("successfulMsgs")
                && !tsKvEntry.getKey().equals("timeoutMsgs")
                && !tsKvEntry.getKey().equals("failedMsgs")
                && !tsKvEntry.getKey().equals("tmpTimeout")
                && !tsKvEntry.getKey().equals("tmpFailed")) {
            super.insertOne(tsKv);
        }
        return Futures.immediateFuture(null);
    }

    @Override
    public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {
        return Futures.immediateFuture(null);
    }

    @Override
    public ListenableFuture<Void> remove(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
        System.out.println("===================remove=========================");
        if(query == null){
            String wheres = String.format("\"entity_id\"=\'%s\'" , entityId.getId().toString());
            super.deleteByCondition("ts_kv",wheres,null,null);
            return Futures.immediateFuture(null);
        }
        String startTs = DateUtils.timstamp2DateTimeStr(query.getStartTs());
        String endTs = DateUtils.timstamp2DateTimeStr(query.getEndTs());
        String wheres = String.format("\"key\"=\'%s\',\"entity_id\"=\'%s\'" , query.getKey() , entityId.getId().toString());
        super.deleteByCondition("ts_kv",wheres,startTs,endTs);
        return Futures.immediateFuture(null);
    }

    @Override
    public ListenableFuture<Void> removePartition(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) {
        System.out.println("===================removePartition=========================");
        return Futures.immediateFuture(null);
    }


    public TsKvEntry conversionTsKv(TsKv kv){
        Long lastUpdateTs = null;
        try {
            lastUpdateTs = DateUtils.DateTimeStr2timstamp(kv.getTime());
        } catch (ParseException e) {
            e.printStackTrace();
        }
        if (null != kv.getStrV() && (!kv.getStrV().isEmpty() || !kv.getJsonV().isEmpty())) {
            KvEntry attrValue = new StringDataEntry(kv.getKey(), kv.getStrV());
            AttributeKvEntry attr = new BaseAttributeKvEntry(attrValue, lastUpdateTs);
            return new BasicTsKvEntry(lastUpdateTs, attr);
        }else if (null != kv.getDblV() && !kv.getDblV().isNaN()) {
            KvEntry attrValue = new DoubleDataEntry(kv.getKey(), kv.getDblV());
            AttributeKvEntry attr = new BaseAttributeKvEntry(attrValue, lastUpdateTs);
            return new BasicTsKvEntry(lastUpdateTs, attr);
        }else if (null != kv.getBoolV()) {
            KvEntry attrValue = new BooleanDataEntry(kv.getKey(), kv.getBoolV());
            AttributeKvEntry attr = new BaseAttributeKvEntry(attrValue, lastUpdateTs);
            return new BasicTsKvEntry(lastUpdateTs, attr);
        }else if (null != kv.getLongV()) {
            KvEntry attrValue = new LongDataEntry(kv.getKey(), kv.getLongV());
            AttributeKvEntry attr = new BaseAttributeKvEntry(attrValue, lastUpdateTs);
            return new BasicTsKvEntry(lastUpdateTs, attr);
        }
        return null;
    }
}
